using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.IO;
using System.Windows.Forms;
using WindowsApplication4.Properties;
using System.Threading;

namespace WindowsApplication4
{
    public class World
    {
        public class CONSTS
        {
            public class PlayersTable
            {
                public class Columns
                {
                    public static int IDIndex = 0;
                    public static string IDMame = "id";
                    public static int NameIndex = 1;
                    public static string NameName = "name";
                    public static int AllyIndex = 2;
                    public static int PointsIndex = 4;
                    
                }
            }
            public class VillagesTable
            {
                public class Columns
                {

                    public static String IdName = "ID";

                    public static int IdIndex = 0;
                    public static int TribeIndex = 4;
                    public static string TribeName = "tribe";
                    public static int XIndex = 2;
                    public static int YIndex = 3;
                    public static int NameIndex = 1;
                    public static int PointsIndex = 5;
                }
            }
            public class TribesTable
            {
                public class Columns
                {
                    public static int IDIndex = 0;
                    public static string IDName = "ID";
                    public static int NameIndex = 1;
                    public static int TagIndex = 2;
                    public static string TagName = "tag";
                }
            }
            public class ReportsTable
            {
                public class Columns
                {
                    public static int VillageIDIndex = 0;
                    public static int LastCheckedOn = 1;
                    public static int ReportFrom = 2;
                    public static int ReportOn = 3;
                    public static int in_troop_startIndex = 4;
                    public static int out_troop_startIndex = 13;
                    public static int WallIndex = 22;

                    
                }
            }
            public class Relations
            {
                public static string PlayersToVillages = "PlayersToVillages";
                public static string VillagesToReports = "VillagesToReports";
                
            }
            //public static string File = @".\w3.xml";

        }

        public string fileName; 


        public System.Collections.Generic.SortedDictionary<string, Unit> Units;
        public String Name;

        public DataTable dtPlayers;
        public DataTable dtVillages;
        public DataTable dtTribes;
        public DataTable dtReports;
        public DataSet dsWorldData;

        private loading frmLoadingForm;
        private Thread thrLoadingFormThread;

        public World(string name,int type)
        {
           
            string serverName = Common.GetServerName(type) + ".";
           
                fileName = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\TW++ Saved Files\" + serverName + name + @"\" + name + ".xml";
                Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\TW++ Saved Files\" + serverName + name);
                Units = new System.Collections.Generic.SortedDictionary<string, Unit>();
                this.Name = name;


        }
        public void init()
        {
            CreateTables();

            if (File.Exists(fileName))
            {
                this.ShowLoadingForm();

                try
                {
                    //TODO: enable gzipped stream!!

                    dsWorldData.ReadXml(fileName, XmlReadMode.IgnoreSchema);
                    EnableConstraits();
                }
                catch (Exception e)
                {
                    error frmError = new error();
                    frmError.textBox1.Text = e.Message + "\n" + e.StackTrace;
                    frmError.ShowDialog();
                }

                Cursor.Current = Cursors.Default;

                //To close loading form, kill thread first
                try
                {
                    this.thrLoadingFormThread.Abort();
                    this.thrLoadingFormThread.Join();
                }
                catch
                {
                    //Do nothing
                }
                this.frmLoadingForm.Close();
            }
            else
            {
                if (System.Windows.Forms.MessageBox.Show("Welcome to TW++.\n\n"
                    + "TW++ downloads world data from tribal wars server to your computer.\n\n"
                    + "No data file has been found which means you need to download the data from the server.\n\n"
                + "Approximatelly 7MB of data will be downloaded so depending on your internet connection, this can take several minutes\n\n"
                + "Are you ready to download? if you choose no, application will exit"
                , "Welcome to TW++! You need to download world data"
                , System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Exclamation) == System.Windows.Forms.DialogResult.Yes)
                {
                    if (!DownloadData())
                    {
                        throw new NoDownloadDataException();
                    }
                }
                else
                {
                    throw new NoDownloadDataException();
                }

            }

        }
        private void CreateTables()
        {
            dtPlayers = new DataTable("Players");
            dtPlayers.Columns.Add("ID", System.Type.GetType("System.Int32"));
            dtPlayers.Columns.Add("name", System.Type.GetType("System.String"));
            dtPlayers.Columns.Add("ally", System.Type.GetType("System.Int32"));
            dtPlayers.Columns.Add("v", System.Type.GetType("System.Int32")); // villages
            dtPlayers.Columns.Add("p", System.Type.GetType("System.Int32")); // points
            dtPlayers.Columns.Add("r", System.Type.GetType("System.Int32")); //rank

            dtVillages = new DataTable("Villages");
            dtVillages.Columns.Add("ID", System.Type.GetType("System.Int32"));
            dtVillages.Columns.Add("name", System.Type.GetType("System.String"));
            dtVillages.Columns.Add("x", System.Type.GetType("System.Int32"));
            dtVillages.Columns.Add("y", System.Type.GetType("System.Int32"));
            dtVillages.Columns.Add("tribe", System.Type.GetType("System.Int32"));
            dtVillages.Columns.Add("p", System.Type.GetType("System.Int32")); //points
            dtVillages.Columns.Add("r", System.Type.GetType("System.Int32")); // rank

            dtTribes = new DataTable("Tribes");
            dtTribes.Columns.Add("ID", System.Type.GetType("System.Int32"));
            dtTribes.Columns.Add("name", System.Type.GetType("System.String"));
            dtTribes.Columns.Add("tag", System.Type.GetType("System.String"));
            dtTribes.Columns.Add("m", System.Type.GetType("System.Int32")); //members
            dtTribes.Columns.Add("v", System.Type.GetType("System.Int32")); //villages
            dtTribes.Columns.Add("p", System.Type.GetType("System.Int32")); //points
            dtTribes.Columns.Add("ap", System.Type.GetType("System.Int32"));// all_points
            dtTribes.Columns.Add("r", System.Type.GetType("System.Int32")); // rank

            dtReports = new DataTable("Reports");
            dtReports.Columns.Add("VillageID", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("LastCheckedOn", System.Type.GetType("System.DateTime"));
            dtReports.Columns.Add("ReportFrom", System.Type.GetType("System.String"));
            dtReports.Columns.Add("ReportOn", System.Type.GetType("System.DateTime"));
            dtReports.Columns.Add("in_sp", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_sw", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_axe", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_scout", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_lc", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_hc", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_ram", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_cat", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("in_noble", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_sp", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_sw", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_axe", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_scout", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_lc", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_hc", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_ram", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_cat", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("out_noble", System.Type.GetType("System.Int32"));
            dtReports.Columns.Add("wall", System.Type.GetType("System.Int32"));

            dsWorldData = new DataSet();
            dsWorldData.CaseSensitive = true;

            dsWorldData.Tables.Add(dtPlayers);
            dsWorldData.Tables.Add(dtVillages);
            dsWorldData.Tables.Add(dtTribes);
            dsWorldData.Tables.Add(dtReports);


        }

        public void AddReport(Village vill, DataRow dr)
        {
            dtReports.Rows.Add(new object[] {vill.ID, DateTime.Now, dr[1], dr[2]
                , dr[3], dr[4], dr[5], dr[6], dr[7], dr[8], dr[9], dr[10], dr[11] 
                , dr[12], dr[13], dr[14], dr[15], dr[16], dr[17], dr[18], dr[19], dr[20]
                , dr[21] });
            dtReports.AcceptChanges();
        }

        private void EnableConstraits()
        {


            try            
            {

                //
                // perform integrity checks and clean up first
                //


                //DataRow[] drTribes;
                //foreach (DataRow dr in dtTribes.Rows)
                //{
                //    drTribes = dtTribes.Select("tag = '" + dr[CONSTS.TribesTable.Columns.TagIndex].ToString() + "'");
                //    if (drTribes.Length > 1)
                //    {
                //        int i;
                //        i = 5;
                //    }
                //}

                dtPlayers.PrimaryKey = new DataColumn[] { dtPlayers.Columns[CONSTS.PlayersTable.Columns.NameIndex] };
                dtVillages.PrimaryKey = new DataColumn[] { dtVillages.Columns[CONSTS.VillagesTable.Columns.XIndex]
                , dtVillages.Columns[CONSTS.VillagesTable.Columns.YIndex]};
             
                DataRow[] drPlayers;
                DataRow[] drTribes;
                List<DataRow> remove = new List<DataRow>();
                foreach (DataRow dr in dtTribes.Rows)
                {
                    drTribes = dtTribes.Select(CONSTS.TribesTable.Columns.TagName + "= '" + dr[CONSTS.TribesTable.Columns.TagIndex].ToString().Replace("'","''") + "'");
                    if (drTribes.Length != 1)
                    {
                        remove.Add(dr);
                    }
                }
                foreach (DataRow dr in dtPlayers.Rows)
                {
                    if (dr[CONSTS.PlayersTable.Columns.AllyIndex].ToString() != "0")
                    {
                        drTribes = dtTribes.Select(CONSTS.TribesTable.Columns.IDName + "=" + dr[CONSTS.PlayersTable.Columns.AllyIndex].ToString());

                        if (drTribes.Length != 1 || ExistsInRemove(remove, drTribes))
                        {

                            remove.Add(dr);
                        }

                    }

                }
                foreach (DataRow dr in dtVillages.Rows)
                {
                    drPlayers = dtPlayers.Select(CONSTS.PlayersTable.Columns.IDMame + " = " + dr[CONSTS.VillagesTable.Columns.TribeIndex].ToString());
                    if (drPlayers.Length != 1 || ExistsInRemove(remove, drPlayers))
                    {
                        remove.Add(dr);
                    }
                }

                
                

                if (remove.Count != 0)
                {
                    frmIntegrityFix frm = new frmIntegrityFix(remove);
                    frm.ShowDialog();

                    foreach (DataRow drToRemove in remove)
                    {
                        drToRemove.Delete();
                    }

                }

                if (dtTribes.Select(CONSTS.TribesTable.Columns.IDName + "= 0").Length == 0)
                {

                    dtTribes.Rows.Add(0, "No Allay", "NoAllay", 0, 0, 0, 0, 0);//add this record to handle tribles with no Ally(bad suggestion)
                }
                    dtPlayers.AcceptChanges();
                    dtTribes.AcceptChanges();
                    dtVillages.AcceptChanges();


                //
                // enable constraints
                //
                dtTribes.PrimaryKey = new DataColumn[] { dtTribes.Columns[CONSTS.TribesTable.Columns.TagIndex] };

                dsWorldData.Relations.Add("PlayersToVillages", dtPlayers.Columns[CONSTS.PlayersTable.Columns.IDIndex]
                    , dtVillages.Columns[CONSTS.VillagesTable.Columns.TribeIndex]);

                dsWorldData.Relations.Add("TribesToPlayers", dtTribes.Columns[CONSTS.TribesTable.Columns.IDIndex]
                    , dtPlayers.Columns[CONSTS.PlayersTable.Columns.AllyIndex] );

                dsWorldData.Relations.Add("VillagesToReports", dtVillages.Columns[CONSTS.VillagesTable.Columns.IdIndex]
                    , dtReports.Columns[CONSTS.ReportsTable.Columns.VillageIDIndex]);

                dtVillages.AcceptChanges();
                dtTribes.AcceptChanges();
            }
            catch (Exception e)
            {
                throw e;
            }
        }
        private bool ExistsInRemove(List<DataRow> remove, DataRow[] drs)
        {
            foreach (DataRow dr in drs)
            {
                if (remove.IndexOf(dr) >= 0)
                {
                    return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Same as calling GetPlayer(name)
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public Player Player(string name)
        {
            DataRow drPlayer = dtPlayers.Rows.Find(name);
            if (drPlayer == null)
            {
                return null;
            }
            else
            {
                Player player = new Player(drPlayer);
                return player;
            }
        }

        public Tribe GetTribe(int tribeID)
        {
            DataRow[] drs = dtTribes.Select("ID=" + tribeID.ToString()); ;
            if (drs.Length == 0)
            {
                return null;
            }
            else
            {
                Tribe tribe= new Tribe(drs[0]);
                return tribe;
            }

        }

        /// <summary>
        /// Get player by name
        /// </summary>
        public Player GetPlayer(string playerName)
        {
            return this.Player(playerName);
        }


        public Village Village(int x, int y)
        {
            DataRow drVillage = dtVillages.Rows.Find(new object[] { x, y });
            if (drVillage == null)
            {
                return null;
            }
            else
            {
                Village village = new Village(drVillage);
                return village;
            }
        }
        public Village Village(int id)
        {
            DataRow[] drs = dtVillages.Select(CONSTS.VillagesTable.Columns.IdName + " = '" + id.ToString() + "'");

            if (drs == null || drs.Length == 0)
            {
                return null;
            }
            else
            {
                Village village = new Village(drs[0]);
                return village;
            }
        }

        public bool downloadCanceled = false; // quick and dirty...
        public bool DownloadData()
        {
            TimeSpan OneHourSpan = new TimeSpan(0, 1, 0, 0, 0);
            if (File.Exists(fileName) )
            {
                DateTime date = File.GetLastWriteTime(fileName);

                TimeSpan span = DateTime.Now - date;
                if (span > OneHourSpan)
                {
                    if (System.Windows.Forms.MessageBox.Show("You are about to download the latest world 3 data from server." + Environment.NewLine + Environment.NewLine
                    + "Approximatelly 7MB of data will be downloaded so depending on your internet connection, this can take several minutes" + Environment.NewLine + Environment.NewLine
                    + "Last time you downloaded the data was "
                        + span.Hours.ToString()
                    + " hours ago.\n\nTribalWars team strongly suggest you do not download"
                    + "data more then once an hour\n\nWould you like to continue?"
                    , "Download"
                    , System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Exclamation) != System.Windows.Forms.DialogResult.Yes)
                    {
                        downloadCanceled = true;// quick and dirty...
                        return true;
                    }
                }
                else
                {
                    MessageBox.Show("Tribal Wars team has asked us not to allow downloading data more then once per hour due to server load. Please download in a little while.");
                    downloadCanceled = true;
                    return true ;
                }
            }



          
            //
            // Try to download
            //
            int numberOfErrors = 0;
            string errors = string.Empty;
            while (true)
            {
                try
                {
                    CreateTables();

                    DownloadAndLoadInData();
                    numberOfErrors = 0; // signal that download was successful. 
                }
                catch (Exception e)
                {
                    numberOfErrors++;

                    errors += "----------- Try #" + numberOfErrors.ToString() + "------" + Environment.NewLine;
                    errors += e.Message + Environment.NewLine + Environment.NewLine
                        + e.StackTrace + Environment.NewLine + Environment.NewLine;
                }

                if (numberOfErrors == 1)
                {
                    DialogResult result = MessageBox.Show("We are having trouble downloading data." + Environment.NewLine + Environment.NewLine
                        + "This could be due to temporary network failure but it can also mean that you need to configure your network settings." + Environment.NewLine + Environment.NewLine
                        + "Click OK and then network settings windows will be launched. Ensure all information is correct, click Save and we will attempt download again." + Environment.NewLine + Environment.NewLine
                        , "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);

                    Options frm = new Options();
                    frm.ShowDialog();
                }
                else if (numberOfErrors > 1)
                {
                    DialogResult result = MessageBox.Show("We continue experiencing trouble downloading data." + Environment.NewLine + Environment.NewLine
                        + "Would you like to examine the network settings and try download again?" + Environment.NewLine + Environment.NewLine
                        + "Click OK and then network settings windows will be launched." + Environment.NewLine + Environment.NewLine
                        + "Click Cancel to abort and display an error log. Please email this error log to support" + Environment.NewLine + Environment.NewLine
                        , "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                    if (result == DialogResult.OK)
                    {
                        Options frm = new Options();
                        frm.ShowDialog();
                    }
                    else
                    {
                        //throw new Exception(errors);
                        error frmError = new error();
                        frmError.textBox1.Text = errors;
                        frmError.ShowDialog();

                        //
                        // DOWNLOAD FAILED
                        //
                        return false;
                    }
                }
                else
                {
                    break; // download successful so break from the download loop
                }
            }
 
            //
            // download successful. finish up
            try {
               EnableConstraits();
               dsWorldData.WriteXml(fileName, XmlWriteMode.WriteSchema);
            }
            catch (Exception e)
            {
                error frmError = new error();
                frmError.textBox1.Text = e.Message + "\n" + e.StackTrace;
                frmError.ShowDialog();
                throw e;
            }
            //
            // DOWNLOAD SUCCESSFUL
            //
            return true;
        }
        public void ValidateInt( ref string IntValueAsString)
        {
            if (String.IsNullOrEmpty(IntValueAsString))
            {
                IntValueAsString = "0";
            }
        }
        private void DownloadAndLoadInData() {


            string line;
            string[] splitLine;

            StreamReader objReader=null;

            this.ShowLoadingForm();

            try
            {

                #region w3ally - read in tribes

                CHttpEngine eng = new CHttpEngine();

                objReader = eng.GETData(Settings.Default.ally, "");

                while ((line = objReader.ReadLine()) != null)
                {
                    ///                listBox1.Items.Add(line);
                    splitLine = line.Split(',');
                    splitLine[1] = System.Web.HttpUtility.UrlDecode(splitLine[1]);
                    splitLine[2] = System.Web.HttpUtility.UrlDecode(splitLine[2]);

                    dtTribes.Rows.Add(splitLine);
                }
                dtTribes.Rows.Add(new object[] { 0, "", "", 0, 0, 0, 0, 0 });

                objReader.Close();
                objReader = null; 
                #endregion

                #region tribe
                objReader = eng.GETData(Settings.Default.tribe, "");
                //            objReader = new StreamReader(@".\data\tribe.txt");



                while ((line = objReader.ReadLine()) != null)
                {
                    //                listBox1.Items.Add(line);
                    splitLine = line.Split(',');
                    splitLine[1] = System.Web.HttpUtility.UrlDecode(splitLine[1]);
                   // splitLine[2] = System.Web.HttpUtility.UrlDecode(splitLine[2]);

                    ValidateInt(ref splitLine[0]);
                    ValidateInt(ref splitLine[2]);
                    ValidateInt(ref splitLine[3]);
                    ValidateInt(ref splitLine[4]);
                    ValidateInt(ref splitLine[5]);
                        dtPlayers.Rows.Add(splitLine);
                   

                }
                dtPlayers.Rows.Add(new object[] { 0, "", 0, 0, 0, 0 });

                objReader.Close();
                objReader = null;
                #endregion

                #region w3villages
                objReader = eng.GETData(Settings.Default.village, "");
                //            objReader = new StreamReader(@".\data\village.txt");


                while ((line = objReader.ReadLine()) != null)
                {
                    ///                listBox1.Items.Add(line);
                    splitLine = line.Split(',');
                    splitLine[1] = System.Web.HttpUtility.UrlDecode(splitLine[1]);
                    ValidateInt(ref splitLine[0]);
                    ValidateInt(ref splitLine[2]);
                    ValidateInt(ref splitLine[3]);
                    ValidateInt(ref splitLine[4]);
                    ValidateInt(ref splitLine[5]);
                    ValidateInt(ref splitLine[6]);

                    dtVillages.Rows.Add(splitLine);

                }

                objReader.Close();
                objReader = null;
                #endregion
            }
            finally
            {
                if (objReader != null)
                {
                    objReader.Close();
                }

                //To close loading form, kill thread first
                try
                {
                    this.thrLoadingFormThread.Abort();
                    this.thrLoadingFormThread.Join();
                }
                catch
                {
                    //Do nothing
                }
            }


        }

        internal void setServerLink(string p)
        {
//            WindowsApplication4.Properties.Settings.Default.ally = p + "/ally.txt";
//            WindowsApplication4.Properties.Settings.Default.village = p + "/village.txt";
//            WindowsApplication4.Properties.Settings.Default.tribe = p + "/tribe.txt";
            WindowsApplication4.Properties.Settings.Default.ally = p + "/ally.txt.gz";
            WindowsApplication4.Properties.Settings.Default.village = p + "/village.txt.gz";
            WindowsApplication4.Properties.Settings.Default.tribe = p + "/tribe.txt.gz";
        }

        private void ShowLoadingForm()
        {
            this.thrLoadingFormThread = new Thread(this.ShowLoadingFormThread);
            this.thrLoadingFormThread.Start();
        }

        private void ShowLoadingFormThread()
        {
            if (this.frmLoadingForm == null || this.frmLoadingForm.IsDisposed)
            {
                this.frmLoadingForm = new loading();
            }

            this.frmLoadingForm.Show();
        }
    }
}
